/*
 * Copyright (c) 2010-2011, The Bishops Arm Project All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.  Redistributions in binary
 * form must reproduce the above copyright notice, this list of conditions and
 * the following disclaimer in the documentation and/or other materials
 * provided with the distribution.  Neither the name of the The Bishops Arm
 * Project nor the names of its contributors may be used to endorse or promote
 * products derived from this software without specific prior written
 * permission.

 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package se.joinenea.bishopsarm;

import java.io.IOException;
import java.util.UUID;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.PowerManager;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class BishopsArm extends Activity implements OnClickListener {

    private static final String TAG = "BishopsArm";

    private boolean rememberToTurnOffBluetoothAfter = false;

    private static final int REQUEST_CONNECT_DEVICE = 1;
    private static final int REQUEST_ENABLE_BT = 2;
    private static final int BT_SOCKET_CONNECTED = 3;

    private TextView wViewO = null;

    // Socket socket = null;
    private BluetoothSocket socket = null;
    private Button buttonConnectNXT;
    private Button buttonStartGame;

    private NXTBot bot;
    private PowerManager.WakeLock wl;
    private final BluetoothAdapter btAdapter = BluetoothAdapter
            .getDefaultAdapter();

    private Game game;

    public BishopsArm() {
        super();
    }

    private void obtainScreenDimWakeLock() {
        PowerManager pm =
                (PowerManager) getSystemService(Context.POWER_SERVICE);
        wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, TAG);
    }

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        wViewO = (TextView) findViewById(R.id.wboxo);

        buttonConnectNXT = (Button) findViewById(R.id.buttonConnectNXT);
        buttonConnectNXT.setOnClickListener(this);

        buttonStartGame = (Button) findViewById(R.id.buttonStartGame);
        buttonStartGame.setEnabled(false);
        buttonStartGame.setOnClickListener(this);

        obtainScreenDimWakeLock();

        if (btAdapter == null) {
            // Device does not support Bluetooth
            String msg = "Bluetooth is not available";
            showMessage(msg);
            wViewO.setText(msg);
            // finish();
            return;
        } else {
            wViewO.setText("");
        }

        // TODO: We need some way to make sure that bt is really connected to
        // the device before setting up our sockets. It's not enough to just run
        // the connect procedure in a separate thread (race conditions,
        // resulting in spurious exceptions). We therefore request that
        // bluetooth is enabled before running this app, until we have fixed
        // this issue
        if (!btAdapter.isEnabled()) {
            String msg =
                    "Turn on bluetooth before running "
                            + getString(R.string.app_name);
            showMessage(msg);
            wViewO.setText(msg);
            finish();
        }
        // if (!btAdapter.isEnabled()) {
        // rememberToTurnOffBluetoothAfter = true;
        // Intent enableBtIntent =
        // new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        // startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        // }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
        case REQUEST_ENABLE_BT:
            handleBTEnableResult(resultCode);
            break;

        case REQUEST_CONNECT_DEVICE:
            handleBTConnectResult(resultCode, data);
            break;

        case BT_SOCKET_CONNECTED:
            // TODO: Extract socket from Bundle.
            break;
        }
    }

    private void handleBTConnectResult(int resultCode, Intent data) {
        if (resultCode == RESULT_OK) {
            Bundle extra = data.getExtras();
            showMessage("Success: "
                    + extra.getString(DeviceListActivity.EXTRA_DEVICE_NAME)
                    + " ("
                    + extra.getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS)
                    + ")");

            BluetoothDevice device =
                    (BluetoothDevice) extra
                            .getParcelable(DeviceListActivity.EXTRA_DEVICE);
            if (device != null) {
                connectRobot(device);
                enableGameButton();
            }
        }
    }

    private void enableGameButton() {
        buttonStartGame.setEnabled(true);
    }

    private void disableGameButton() {
        buttonStartGame.setEnabled(false);
    }

    private void handleBTEnableResult(int resultCode) {
        if (resultCode == RESULT_OK) {
            buttonConnectNXT.setEnabled(true);
            wViewO.setText("");
        } else {
            buttonConnectNXT.setEnabled(false);
            String msg = "Unable to Enable Bluetooth.";
            showMessage(msg);
            wViewO.setText(msg);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        wl.acquire();
        if (bot != null) {
            bot.registerSensorListener();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        wl.release();
    }

    @Override
    protected void onStop() {
        if (bot != null) {
            bot.unregisterSensorListener();
        }

        if (rememberToTurnOffBluetoothAfter) {
            if (btAdapter.isEnabled()) {
                // TODO: Add polite question whether to turn off bluetooth or
                // not
                btAdapter.disable();
                rememberToTurnOffBluetoothAfter = false;
            }
        }
        super.onStop();
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
        case R.id.buttonConnectNXT:
            if (!isRobotConnected()) {
                startDeviceListActivity();
            } else {
                disconnectRobot();
                buttonConnectNXT.setText(R.string.connect2NXT);
                disableGameButton();
            }
            break;
        case R.id.buttonStartGame:
            if (!isGameRunning()) {
                startGame();
            } else {
                stopGame();
            }
        }
    }

    private void startDeviceListActivity() {
        Intent connectBT = new Intent(this, DeviceListActivity.class);
        startActivityForResult(connectBT, REQUEST_CONNECT_DEVICE);
    }

    boolean isRobotConnected() {
        return bot != null;
    }

    private void disconnectRobot() {
        if (isGameRunning()) {
            stopGame();
        }
        game = null;
        try {
            bot.stop();
            bot.unregisterSensorListener();
        } catch (IOException e) {
            showMessage(e.getMessage());
        } finally {
            bot = null;
        }
        try {
            if (socket != null) {
                socket.close();
            }
        } catch (IOException e) {
            showMessage(e.getMessage());
        }
        socket = null;
    }

    private void startGame() {
        if (game != null) {
            game.start();
        }
    }
    private void stopGame() {
        game.stop();
    }

    private boolean isGameRunning() {
        return game != null && game.isRunning();
    }

    void showMessage(String message) {
        Log.d(TAG, message);
        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
    }

    private void connectRobot(BluetoothDevice device) {
        final UUID MY_UUID =
                UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
        // Get a BluetoothSocket to connect with the given BluetoothDevice
        try {
            // MY_UUID is the app's UUID string, also used by the server
            // code
            socket = device.createRfcommSocketToServiceRecord(MY_UUID);
            Log.d(TAG, "Got RFcomm Socket.");
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(TAG, e.getMessage());
        }

        Log.e(TAG, "Got connection!");
        // Cancel discovery because it will slow down the connection
        // if (btAdapter.getName() != null) {
        // Log.d(TAG, "connectRobot():" + btAdapter.getName() + " ("
        // + btAdapter.getAddress() + ")");
        // }
        btAdapter.cancelDiscovery();
        try {
            // Connect the device through the socket. This will block
            // until it succeeds or throws an exception
            if (socket == null) {
                Log.e(TAG, "Null socket!");
                finish();
                return;
            }
            socket.connect();
        } catch (IOException connectException) {
            // Unable to connect; close the socket and get out
            Log.e(TAG, "IOException in run()", connectException);
            try {
                socket.close();
            } catch (IOException closeException) {
            }
            finish();
            return;
        }

        // TODO: Do work to manage the connection (in a separate thread)
        manageConnectedSocket(socket);
    }

    private void manageConnectedSocket(BluetoothSocket socket) {
        try {
            bot = new NXTBot(socket, this);
            // Next line must be called from the "master" thread or else
            // spurious exceptions will occur
            buttonConnectNXT.setText(R.string.disconnectNXT);
            game = new ColorGame(this, bot);
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(TAG, "IOException caught!!!");
        }
    }
}
